﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.Design;
using System.Linq;
using System.Security.Policy;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Windows.Input;
using System.Windows.Media;
using AIStudio.Wpf.DiagramDesigner;
using AIStudio.Wpf.DiagramDesigner.Geometrys;
using AIStudio.Wpf.DiagramDesigner.Helpers;
using AIStudio.Wpf.DiagramDesigner.Models;
using AIStudio.Wpf.DiagramDesigner.Serializable;
using AIStudio.Wpf.DiagramDesigner.Serializable.ViewModels;
using AIStudio.Wpf.Mind.Controls;
using AIStudio.Wpf.Mind.Helpers;
using AIStudio.Wpf.Mind.Models;

namespace AIStudio.Wpf.Mind.ViewModels
{
    public class MindNode : DiagramItemViewModel
    {
        public MindNode() : this(null)
        {
        }

        public MindNode(IDiagramViewModel root) : base(root)
        {

        }

        public MindNode(IDiagramViewModel root, SelectableItemBase designer) : base(root, designer)
        {
            InitLayout(false);
        }

        public MindNode(IDiagramViewModel root, SerializableItem serializableItem, string serializableType) : base(root, serializableItem, serializableType)
        {
            InitLayout(false);
        }


        public override SelectableItemBase GetSerializableObject()
        {
            return new MindNodeDesignerItem(this);
        }

        public override DiagramNode ToDiagram()
        {
            var mindNodeModel = new MindNodeModel();

            mindNodeModel.MindType = MindType;
            mindNodeModel.MindTheme = MindTheme;
            mindNodeModel.Spacing = Spacing;
            mindNodeModel.Offset = Offset;
            mindNodeModel.IsExpanded = IsExpanded;
            mindNodeModel.LinkInfoModel = new LinkInfoModel(LinkInfo);
            mindNodeModel.ImageInfoModel = new ImageInfoModel(ImageInfo);
            mindNodeModel.Remark = Remark;
            mindNodeModel.Priority = Priority;
            mindNodeModel.Rate = Rate;
            if (Tags != null)
            {
                mindNodeModel.Tags = new List<string>(Tags);
            }
            return mindNodeModel;
        }

        protected override void Init(IDiagramViewModel root, bool initNew)
        {
            base.Init(root, initNew);

            EnabledForConnection = false;
            AlwayForResized = true;
            CustomText = true;
            IsInnerConnector = true;

            AddChildCommand = (Root as IMindDiagramViewModel)?.AddChildCommand;
            AddParentCommand = (Root as IMindDiagramViewModel)?.AddParentCommand;
            AddPearCommand = (Root as IMindDiagramViewModel)?.AddPearCommand;
            DeleteCommand = (Root as IMindDiagramViewModel)?.DeleteCommand;
            MoveForwardCommand = (Root as IMindDiagramViewModel)?.MoveForwardCommand;
            MoveBackCommand = (Root as IMindDiagramViewModel)?.MoveBackCommand;
            ExportCommand = (Root as IMindDiagramViewModel)?.ExportCommand;
            ImportCommand = (Root as IMindDiagramViewModel)?.ImportCommand;
            BuildMenuOptions();
            Tags = new ObservableCollection<string>();
        }

        protected override void InitNew()
        {
            this.ClearConnectors();
        }

        public void InitLayout(bool initAppearance)
        {
            IsLoaded = false;
            var layout = TypeHelper.GetType(this.MindType.ToString() + "Layout");
            MindLayout = layout != null ? (System.Activator.CreateInstance(layout) as IMindLayout) : new MindLayout();
            MindLayout.Appearance(this, MindTheme, initAppearance);
            IsLoaded = true;
        }

        public void InitConnectionLayout()
        {
            var connector = Root?.Items.OfType<ConnectionViewModel>().Where(p => p.IsFullConnection).FirstOrDefault(p => p.SinkConnectorInfoFully.DataItem == this);
            if (connector != null)
            {
                MindLayout?.GetOrSetConnectionViewModel(connector.SourceConnectorInfoFully.DataItem as MindNode, connector.SinkConnectorInfoFully.DataItem as MindNode, connector);
            }
            else if (ParentNode != null)
            {
                connector = MindLayout?.GetOrSetConnectionViewModel(ParentNode, this, null);
                Root?.Add(new SelectableDesignerItemViewModelBase[] { connector });
                connector.ZIndex = -1;
                connector.IsSelected = false;
            }
        }

        public void UpdatedLayout()
        {
            MindLayout?.UpdatedLayout(GetLevel0Node());
        }

        public void ThemeChange()
        {
            MindThemeHelper.ThemeChange(this, MindTheme);
        }

        protected override void LoadDesignerItemViewModel(SelectableItemBase designerbase)
        {
            base.LoadDesignerItemViewModel(designerbase);

            if (designerbase is MindNodeDesignerItem designer)
            {
                MindType = designer.MindType;
                MindTheme = designer.MindTheme;
                Spacing = designer.Spacing;
                Offset = designer.Offset;
                IsExpanded = designer.IsExpanded;
                if (string.IsNullOrEmpty(designer.LinkInfoItem?.Url) && string.IsNullOrEmpty(designer.LinkInfoItem?.Text))
                {
                    LinkInfo = null;
                }
                else
                {
                    LinkInfo = new LinkInfo(designer.LinkInfoItem?.Url, designer.LinkInfoItem?.Text);
                }
                if (string.IsNullOrEmpty(designer.ImageInfoItem?.Url) && string.IsNullOrEmpty(designer.ImageInfoItem?.Text))
                {
                    ImageInfo = null;
                }
                else
                {
                    ImageInfo = new ImageInfo(designer.ImageInfoItem?.Url, designer.ImageInfoItem?.Text);
                }
                Remark = designer.Remark;
                if (double.TryParse(designer.Priority ?? "", out var priority))
                {
                    Priority = priority;
                }
                if (double.TryParse(designer.Rate ?? "", out var rate))
                {
                    Rate = rate;
                }
                if (designer.Tags != null)
                {
                    Tags = new ObservableCollection<string>(designer.Tags);
                }
            }
        }

        #region 属性
        private MindType _mindType = MindType.Mind;
        public MindType MindType
        {
            get
            {
                return GetLevel0Node()._mindType;
            }
            set
            {
                GetLevel0Node()._mindType = value;
            }
        }

        private MindTheme _mindTheme;
        public MindTheme MindTheme
        {
            get
            {
                return GetLevel0Node()._mindTheme;
            }
            set
            {
                GetLevel0Node()._mindTheme = value;
            }
        }

        public IMindLayout MindLayout
        {
            get; set;
        }

        public MindNode ParentNode
        {
            get
            {
                return Parent as MindNode;
            }
        }

        public MindNode RootNode
        {
            get
            {
                return GetLevel0Node();
            }
        }

        public int NodeLevel
        {
            get
            {
                if (ParentNode == null)
                {
                    return 0;
                }
                else
                {
                    return ParentNode.NodeLevel + 1;
                }
            }
        }

        private bool _isExpanded = true;
        [CanDo]
        public bool IsExpanded
        {
            get
            {
                return _isExpanded;
            }
            set
            {
                SetProperty(ref _isExpanded, value);
            }
        }

        public SizeBase Spacing
        {
            get; set;
        } = new SizeBase(15, 15);


        private ObservableCollection<MindNode> _children = new ObservableCollection<MindNode>();
        public ObservableCollection<MindNode> Children
        {
            get
            {
                return _children;
            }
            set
            {
                SetProperty(ref _children, value);
            }
        }

        public SizeBase SizeWithSpacing
        {
            get
            {
                return this.Size.Add(Spacing.Width * 2, Spacing.Height * 2);
            }
        }

        public SizeBase DesiredSize
        {
            get; set;
        }

        public PointBase DesiredPosition
        {
            get; set;
        }

        public PointBase DesiredMiddlePosition
        {
            get
            {
                return new PointBase(this.DesiredPosition.X + ItemWidth / 2, this.DesiredPosition.Y + ItemHeight / 2);
            }
        }

        public PointBase Offset
        {
            get; set;
        }

        private ConnectorOrientation _connectorOrientation = ConnectorOrientation.Left;
        public ConnectorOrientation ConnectorOrientation
        {
            get
            {
                return _connectorOrientation;
            }
            set
            {
                SetProperty(ref _connectorOrientation, value);
            }
        }

        public bool LayoutUpdating
        {
            get; set;
        }
        #endregion

        #region 附加信息属性
        private LinkInfo _linkInfo;
        [CanDo]
        public LinkInfo LinkInfo
        {
            get
            {
                return _linkInfo;
            }
            set
            {
                SetProperty(ref _linkInfo, value);
            }
        }

        private ImageInfo _imageInfo;
        [CanDo]
        public ImageInfo ImageInfo
        {
            get
            {
                return _imageInfo;
            }
            set
            {
                SetProperty(ref _imageInfo, value);
            }
        }

        private string _remark;
        [CanDo]
        [Browsable(true)]
        public string Remark
        {
            get
            {
                return _remark;
            }
            set
            {
                SetProperty(ref _remark, value);
            }
        }

        private double? _priority;
        [CanDo]
        public double? Priority
        {
            get
            {
                return _priority;
            }
            set
            {
                SetProperty(ref _priority, value);
            }
        }

        private double? _rate;
        [CanDo]
        public double? Rate
        {
            get
            {
                return _rate;
            }
            set
            {
                SetProperty(ref _rate, value);
            }
        }

        private ObservableCollection<string> _tags;
        [CanDo]
        public ObservableCollection<string> Tags
        {
            get
            {
                return _tags;
            }
            set
            {
                if (_tags != null)
                {
                    _tags.CollectionChanged -= _tags_CollectionChanged;
                }
                SetProperty(ref _tags, value);
                if (_tags != null)
                {
                    _tags.CollectionChanged += _tags_CollectionChanged;
                }
            }
        }

        private void _tags_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
        {
            if (e.NewItems != null)
            {
                foreach (var item in e.NewItems.OfType<string>())
                {
                    var width = GetTextDisplayWidthHelper.GetTextDisplayWidth(item, new FontFamily(FontViewModel.FontFamily), FontViewModel.FontStyle, FontViewModel.FontWeight, FontViewModel.FontStretch, 12) + 6;
                    ItemWidth += width;
                }
            }

            if (e.OldItems != null)
            {
                foreach (var item in e.OldItems.OfType<string>())
                {
                    var width = GetTextDisplayWidthHelper.GetTextDisplayWidth(item, new FontFamily(FontViewModel.FontFamily), FontViewModel.FontStyle, FontViewModel.FontWeight, FontViewModel.FontStretch, 12) + 6;
                    ItemWidth -= width;
                }
            }
        }
        #endregion

        #region 命令
        public ICommand AddParentCommand
        {
            get; private set;
        }

        public ICommand AddChildCommand
        {
            get; private set;
        }

        public ICommand AddPearCommand
        {
            get; private set;
        }

        public ICommand DeleteCommand
        {
            get; private set;
        }

        public ICommand MoveForwardCommand
        {
            get; private set;
        }

        public ICommand MoveBackCommand
        {
            get; private set;
        }

        public ICommand ExportCommand
        {
            get; private set;
        }

        public ICommand ImportCommand
        {
            get; private set;
        }
        #endregion

        #region 菜单
        private void BuildMenuOptions()
        {
            menuOptions = new ObservableCollection<CinchMenuItem>();
            CinchMenuItem menuItem = new CinchMenuItem();
            menuItem.Text = "下级";
            menuItem.Command = AddChildCommand;
            menuItem.CommandParameter = this;
            menuOptions.Add(menuItem);
            menuItem = new CinchMenuItem();
            menuItem.Text = "同级";
            menuItem.Command = AddPearCommand;
            menuItem.CommandParameter = this;
            menuOptions.Add(menuItem);
            menuItem = new CinchMenuItem();
            menuItem.Text = "上级";
            menuItem.Command = AddParentCommand;
            menuItem.CommandParameter = this;
            menuOptions.Add(menuItem);
            menuItem = new CinchMenuItem();
            menuItem.Text = "前移";
            menuItem.Command = MoveForwardCommand;
            menuItem.CommandParameter = this;
            menuOptions.Add(menuItem);
            menuItem = new CinchMenuItem();
            menuItem.Text = "后移";
            menuItem.Command = MoveBackCommand;
            menuItem.CommandParameter = this;
            menuOptions.Add(menuItem);
            menuItem = new CinchMenuItem();
            menuItem.Text = "删除";
            menuItem.Command = DeleteCommand;
            menuItem.CommandParameter = this;
            menuOptions.Add(menuItem);
            menuItem = new CinchMenuItem();
            menuItem.Text = "导出节点";
            menuItem.Command = ExportCommand;
            menuItem.CommandParameter = this;
            menuOptions.Add(menuItem);
            menuItem = new CinchMenuItem();
            menuItem.Text = "导入节点";
            menuItem.Command = ImportCommand;
            menuItem.CommandParameter = this;
            menuOptions.Add(menuItem);
        }
        #endregion

        #region 操作  
        public void AddTo(MindNode parent, int index = -1, bool isSelected = true)
        {
            //parent为空，为根节点
            if (index >= 0)
            {
                parent?.Children.Insert(index, this);
            }
            else
            {
                parent?.Children.Add(this);
            }
            this.Parent = parent;
            this.ParentId = parent?.Id ?? Guid.Empty;
            this.Offset = parent?.Offset ?? new PointBase();
            this.InitLayout(true);//因为节点的层级不同的样式，所以需要Parent确定后才能初始化
            this.InitConnectionLayout();

            Root?.Add(this);

            if (isSelected)
            {
                if (parent != null)
                {
                    parent.IsSelected = false;
                }
                this.IsSelected = true;
            }
        }

        public void RemoveFrom()
        {
            this.IsLoaded = false;
            if (this.ParentNode != null)
            {
                this.ParentNode.Children.Remove(this);
            }
            var connectors = Root?.Items.OfType<ConnectionViewModel>().Where(p => p.SinkConnectorInfoFully?.DataItem == this).ToList();

            Root?.Delete(this);
            Root?.Delete(connectors);
        }
        #endregion

        #region 属性改变
        protected override void Item_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (IsLoaded == false) return;

            if (GetLevel0Node()?.LayoutUpdating == true) return;

            Interlocked.Increment(ref Root.DoCommandManager.BeginDo);
            switch (e.PropertyName)
            {
                case nameof(Left):
                    {
                        if (e is ValuePropertyChangedEventArgs valuePropertyChangedEventArgs)
                        {
                            UpdateOffsetX((double)valuePropertyChangedEventArgs.OldValue, (double)valuePropertyChangedEventArgs.NewValue);
                        }
                        break;
                    }
                case nameof(Top):
                    {
                        if (e is ValuePropertyChangedEventArgs valuePropertyChangedEventArgs)
                        {
                            UpdateOffsetY((double)valuePropertyChangedEventArgs.OldValue, (double)valuePropertyChangedEventArgs.NewValue);
                        }
                        break;
                    }
                case nameof(IsExpanded):
                case nameof(ItemWidth):
                case nameof(ItemHeight):
                    {

                        UpdatedLayout();
                        break;
                    }
                case nameof(NodeLevel):
                    MindLayout?.Appearance(this);
                    break;
                case nameof(Text):
                case nameof(Rate):
                case nameof(Priority):
                case nameof(Remark):
                case nameof(LinkInfo):
                case nameof(ImageInfo):
                    {
                        SetItemWidthHeight();
                        break;
                    }
                case nameof(FontViewModel.FontSize):
                    SetItemWidthHeight();
                    break;
            }
            Interlocked.Decrement(ref Root.DoCommandManager.BeginDo);
        }
        #endregion

        #region 布局相关
        public MindNode GetLevel0Node()
        {
            var node = this;
            while (node.Parent is MindNode mindNode)
            {
                node = mindNode;
            }
            return node;
        }

        public MindNode GetLevel1Node()
        {
            var node = this;
            while (node.Parent is MindNode mindNode)
            {
                if (node.NodeLevel == 1)
                {
                    break;
                }
                node = mindNode;
            }
            return node;
        }

        public List<MindNode> GetParent()
        {
            var node = this;
            List<MindNode> mindnode = new List<MindNode>();
            while (node.ParentNode != null)
            {
                mindnode.Add(node.ParentNode);
                node = node.ParentNode;
            }
            return mindnode;
        }

        public List<MindNode> GetChildren(bool self = false)
        {
            List<MindNode> mindnode = new List<MindNode>();
            if (self)
            {
                mindnode.Add(this);
            }

            if (this.Children != null)
            {
                foreach (var child in this.Children)
                {
                    mindnode.Add(child);
                    mindnode.AddRange(child.GetChildren());
                }
            }
            return mindnode;
        }

        public string GetChildrenText(bool self = false)
        {
            StringBuilder sb = new StringBuilder();
            List<MindNode> mindnode = new List<MindNode>();
            if (self)
            {
                sb.AppendLine(this.Text);
            }

            if (this.Children != null)
            {
                foreach (var child in this.Children)
                {
                    sb.AppendLine($"{new String('\t', child.NodeLevel)}{child.Text}");
                    sb.Append(child.GetChildrenText());
                }
            }
            return sb.ToString();
        }

        protected void UpdateOffsetX(double oldvalue, double newvalue)
        {
            Offset += new VectorBase(newvalue - oldvalue, 0);
        }

        protected void UpdateOffsetY(double oldvalue, double newvalue)
        {
            Offset += new VectorBase(0, newvalue - oldvalue);
        }

        private void SetItemWidthHeight()
        {
            double width = 0;
            double height = 0;
            width += GetTextDisplayWidthHelper.GetTextDisplayWidth(Text, new FontFamily(FontViewModel.FontFamily), FontViewModel.FontStyle, FontViewModel.FontWeight, FontViewModel.FontStretch, FontViewModel.FontSize) + 30;
            width += Rate == null ? 0 : 24;
            width += Priority == null ? 0 : 24;
            width += Remark == null ? 0 : 24;
            width += LinkInfo == null ? 0 : 24;

            var defaultTheme = MindThemeHelper.GetNodeDefaultTheme(this);
            if (ImageInfo != null)
            {
                width = Math.Max(width, 160);
                height = defaultTheme.ItemHeight / defaultTheme.FontSize * FontViewModel.FontSize + 135;
            }
            else
            {
                height = defaultTheme.ItemHeight / defaultTheme.FontSize * FontViewModel.FontSize;
            }

            ItemWidth = width;
            ItemHeight = height;

        }

        public override void AddToSelection(bool selected, bool clearother)
        {
            if (clearother)
            {
                foreach (SelectableDesignerItemViewModelBase item in Root.SelectedItems.ToList())
                {
                    if (item != this)
                    {
                        item.RemoveFromSelection();
                    }
                }
            }

            IsSelected = selected;
        }

        protected override void ExecuteExitEditCommand(object param)
        {
            Root.Next();
        }
        #endregion
    }

    public class LinkInfo
    {
        public string Url
        {
            get; set;
        }

        public string Text
        {
            get; set;
        }

        public LinkInfo()
        {

        }

        public LinkInfo(string url, string text)
        {
            Url = url;
            Text = text;
        }

        public LinkInfo(LinkInfo info)
        {
            Url = info?.Url;
            Text = info?.Text;
        }
    }

    public class ImageInfo
    {
        public string Url
        {
            get; set;
        }

        public string Text
        {
            get; set;
        }

        public ImageInfo()
        {

        }

        public ImageInfo(string url, string text)
        {
            Url = url;
            Text = text;
        }

        public ImageInfo(ImageInfo info)
        {
            Url = info?.Url;
            Text = info?.Text;
        }
    }
}
